package com.mldong.modules.sys.service.impl;

import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mldong.auth.ITokenStore;
import com.mldong.base.CommonPage;
import com.mldong.base.YesNoEnum;
import com.mldong.context.constant.ConstantContextHolder;
import com.mldong.exception.ServiceException;
import com.mldong.holder.LoginUserHolder;
import com.mldong.modules.sys.dto.LoginParam;
import com.mldong.modules.sys.dto.UserPageParam;
import com.mldong.modules.sys.dto.UserParam;
import com.mldong.modules.sys.entity.Dept;
import com.mldong.modules.sys.entity.Role;
import com.mldong.modules.sys.entity.User;
import com.mldong.modules.sys.entity.UserRole;
import com.mldong.modules.sys.enums.AuthErrEnum;
import com.mldong.modules.sys.enums.SysErrEnum;
import com.mldong.modules.sys.enums.UserAdminTypeEnum;
import com.mldong.modules.sys.mapper.*;
import com.mldong.modules.sys.service.UserRoleService;
import com.mldong.modules.sys.service.UserService;
import com.mldong.modules.sys.vo.LoginVO;
import com.mldong.modules.sys.vo.UserVO;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    private final ITokenStore tokenStore;
    private final MenuMapper menuMapper;
    @Value("${g.defaultPassword:123456}")
    private String defaultPassword;
    private final UserRoleMapper userRoleMapper;
    private final DeptMapper deptMapper;
    private final RoleMapper roleMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean save(UserParam param) {
        param.setId(null);
        // 校验用户名唯一性
        checkUserName(param,false);
        // 校验手机号唯一性
        checkMobilePhone(param,false);
        // 密码加密处理
        String salt = RandomUtil.randomString(8);
        String enPassword = SecureUtil.md5(param.getPassword()+salt);
        // 部分字段由系统设置
        param.setAdminType(UserAdminTypeEnum.COMMON.getCode());
        User user = new User();
        BeanUtil.copyProperties(param, user);
        user.setPassword(enPassword);
        user.setSalt(salt);
        boolean success = super.save(user);
        // ServiceException.throwBiz(9999, "事务异常测试");
        return success;
    }

    @Override
    public boolean update(UserParam param) {
        // 校验用户名唯一性
        checkUserName(param,true);
        // 校验手机号唯一性
        checkMobilePhone(param,true);
        // 密码不允许更新
        param.setPassword(null);
        param.setSalt(null);
        // 不允许外部修改管理类型
        param.setAdminType(null);
        User user = new User();
        BeanUtil.copyProperties(param, user);
        return super.updateById(user);
    }

    @Override
    public CommonPage<UserVO> page(UserPageParam param) {
        IPage<UserVO> page = param.buildMpPage();
        QueryWrapper queryWrapper = param.buildQueryWrapper();
        queryWrapper.ne("t.admin_type",UserAdminTypeEnum.ADMIN);
        if(ObjectUtil.isNotNull(param.getExcludeRoleId())) {
            String applySql = "t.id not in(select u3r.user_id from sys_user_role u3r where u3r.role_id={0})";
            queryWrapper.apply(applySql,param.getExcludeRoleId());
        }
        queryWrapper.eq("t.is_deleted",0);
        List<UserVO> list = baseMapper.selectCustom(page, queryWrapper);
        page.setRecords(list);
        return CommonPage.toPage(page);
    }

    @Override
    public UserVO findById(Long id) {
        UserVO vo = baseMapper.selectByIdCustom(id);
        if(vo!=null){
            String baseUrl = ConstantContextHolder.getImgBaseUrl();
            // 处理头像-追加baseUrl
            if(StrUtil.isNotEmpty(vo.getAvatar()) && !StrUtil.startWith(vo.getAvatar(),"http")){
                vo.setAvatar(baseUrl+vo.getAvatar());
            }
        }
        return vo;
    }

    @Override
    public LoginVO login(LoginParam param) {
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(User::getUserName,param.getUserName());
        User user = baseMapper.selectOne(wrapper);
        if(user == null) {
            ServiceException.throwBiz(AuthErrEnum.USERNAME_OR_PASSWORD_ERR);
        }
        if(ObjectUtil.equals(user.getIsLocked(),YesNoEnum.YES.getCode())){
            ServiceException.throwBiz(AuthErrEnum.USER_LOCKED);
        }
        String enPassword = SecureUtil.md5(param.getPassword()+user.getSalt());
        if(!enPassword.equals(user.getPassword())) {
            ServiceException.throwBiz(AuthErrEnum.USERNAME_OR_PASSWORD_ERR);
        }

        LoginVO vo = userToLoginVO(user);
        // tokenStore.setToken(vo);
        SaLoginParameter parameter = new SaLoginParameter();
        parameter.setExtra(LoginVO.class.getSimpleName(),vo);
        StpUtil.login(user.getId(), parameter);
        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
        String token = tokenInfo.getTokenValue();
        vo.setToken(token);
        return vo;
    }

    @Override
    public void logout(String token) {
        // tokenStore.removeToken(token);
        StpUtil.logout();
    }

    @Override
    public void updateAvatar(UserParam param) {
        User upUser = new User();
        upUser.setId(LoginUserHolder.getUserId());
        upUser.setAvatar(param.getAvatar());
        baseMapper.updateById(upUser);
    }

    @Override
    public void updateInfo(UserParam param) {
        User upUser = new User();
        param.setId(LoginUserHolder.getUserId());
        upUser.setId(LoginUserHolder.getUserId());
        upUser.setRealName(param.getRealName());
        upUser.setNickName(param.getNickName());
        upUser.setSex(param.getSex());
        upUser.setRemark(param.getRemark());
        upUser.setTel(param.getTel());
        upUser.setEmail(param.getEmail());
        if(StrUtil.isNotEmpty(param.getMobilePhone())) {
            checkMobilePhone(param, true);
            upUser.setMobilePhone(param.getMobilePhone());
        }
        baseMapper.updateById(upUser);
    }

    @Override
    public void resetPassword(List<Long> userIds) {
        // 超级管理员不允许被重置密码(adminType=1)
        LambdaQueryWrapper<User> lambdaQueryWrapper = Wrappers.lambdaQuery();
        lambdaQueryWrapper.in(User::getId,userIds)
        .ne(User::getAdminType,UserAdminTypeEnum.ADMIN.getCode());
        List<User> userList = baseMapper.selectList(lambdaQueryWrapper);
        if(CollectionUtil.isEmpty(userList)) return;
        // 1. 获取默认密码
        String sysConfigDefaultPassword = ConstantContextHolder.getSysConfigWithDefault(
                "DEFAULT_PASSWORD",
                String.class,
                defaultPassword
        );
        // 2. 重新生成salt、password
        List<User> upUserList = userList.stream().map(item->{
            User user = new User();
            String salt = RandomUtil.randomString(8);
            String enPassword = SecureUtil.md5(sysConfigDefaultPassword+salt);
            user.setId(item.getId());
            user.setSalt(salt);
            user.setPassword(enPassword);
            return user;
        }).collect(Collectors.toList());
        // 3. 更新
        updateBatchById(upUserList);
    }

    @Override
    public void updatePwd(Long userId, String password, String newPassword) {
        if(ObjectUtil.equals(password,newPassword)){
            ServiceException.throwBiz(AuthErrEnum.NEW_OLD_PASSWORD_NOT_EQ);
        }
        User user = baseMapper.selectById(userId);
        if(user == null){
            ServiceException.throwBiz(AuthErrEnum.USERNAME_OR_PASSWORD_ERR);
        }
        String enPassword = SecureUtil.md5(password+user.getSalt());
        if(!enPassword.equals(user.getPassword())) {
            ServiceException.throwBiz(AuthErrEnum.USERNAME_OR_PASSWORD_ERR);
        }
        User up = new User();
        String newSalt = RandomUtil.randomString(8);
        String newEnPassword = SecureUtil.md5(newPassword+newSalt);
        up.setId(user.getId());
        up.setSalt(newSalt);
        up.setPassword(newEnPassword);
        baseMapper.updateById(up);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void grantRole(Long userId, List<Long> roleIdList) {
        // 根据用户id删除用户角色关系
        userRoleMapper.delete(
                Wrappers.lambdaQuery(UserRole.class)
                .eq(UserRole::getUserId,userId)
        );
        if(CollectionUtil.isEmpty(roleIdList)) return;
        // 批量插入新的用户角色关系
        List<UserRole> userRoleList = roleIdList.stream().map(roleId->{
            UserRole userRole = new UserRole();
            userRole.setUserId(userId);
            userRole.setRoleId(roleId);
            return userRole;
        }).collect(Collectors.toList());
        UserRoleService userRoleService = SpringUtil.getBean(UserRoleService.class);
        userRoleService.saveBatch(userRoleList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void locked(List<Long> userIds) {

        List<User> upList = userIds.stream().map(userId->{
            User up = new User();
            up.setId(userId);
            up.setIsLocked(YesNoEnum.YES.getCode());
            return up;
        }).collect(Collectors.toList());
        updateBatchById(upList);
    }

    @Override
    public void unLocked(List<Long> userIds) {
        List<User> upList = userIds.stream().map(userId->{
            User up = new User();
            up.setId(userId);
            up.setIsLocked(YesNoEnum.NO.getCode());
            return up;
        }).collect(Collectors.toList());
        updateBatchById(upList);
    }

    @Override
    public LoginVO playUser(Long userId) {
        LoginVO currentLogin = LoginUserHolder.get();
        // 1. 查询被扮演用户信息
        User user = baseMapper.selectById(userId);
        LoginVO vo = userToLoginVO(user);
        // 2.追加扮演的额外信息
        // 2.1 isPlayer是否扮演用户
        vo.getExt().put("isPlayer", true);
        // 2.2 playUserAccount 扮演用户用户名
        vo.getExt().put("playUserAccount", currentLogin.getUserName());
        // 2.3 playerToken 扮演用户的token
        // vo.getExt().put("playerToken", currentLogin.getToken());
        vo.getExt().put("playerToken", StpUtil.getTokenValue());
        // 2.4 playerUserId 扮演用户id
        vo.getExt().put("playerUserId", currentLogin.getUserId());
        // tokenStore.setToken(vo);
        // tokenStore.setToken(vo);
        SaLoginParameter parameter = new SaLoginParameter();
        parameter.setExtra(LoginVO.class.getSimpleName(),vo);
        StpUtil.login(user.getId(), parameter);
        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
        String token = tokenInfo.getTokenValue();
        vo.setToken(token);
        return vo;
    }

    @Override
    public LoginVO unPlayUser(String token) {
        //LoginVO vo = tokenStore.getToken(token);
        LoginVO vo = BeanUtil.toBean(StpUtil.getExtra(token,LoginVO.class.getSimpleName()),LoginVO.class);
        //tokenStore.removeToken(token);
        StpUtil.logoutByTokenValue(token);

        // return tokenStore.getToken(vo.getExt().getStr("playerToken"));
        String playerToken = vo.getExt().getStr("playerToken");
        return BeanUtil.toBean(StpUtil.getExtra(playerToken,LoginVO.class.getSimpleName()),LoginVO.class);
    }

    /**
     * 校验用户名唯一性
     * @param param
     * @param isUpdate
     */
    private void checkUserName(UserParam param,boolean isUpdate) {
        LambdaQueryWrapper<User> lambdaQueryWrapper = Wrappers.lambdaQuery();
        lambdaQueryWrapper.eq(User::getUserName,param.getUserName());
        if(isUpdate) {
            // 更新时，需要排除当前id
            lambdaQueryWrapper.ne(User::getId,param.getId());
        }
        long count = baseMapper.selectCount(lambdaQueryWrapper);
        if(count>0) {
            // 抛出一个用户名已存在异常
            ServiceException.throwBiz(SysErrEnum.USER_NAME_EXISTS);
        }
    }
    /**
     * 校验手机号唯一性
     * @param param
     * @param isUpdate
     */
    private void checkMobilePhone(UserParam param,boolean isUpdate) {
        LambdaQueryWrapper<User> lambdaQueryWrapper = Wrappers.lambdaQuery();
        lambdaQueryWrapper.eq(User::getMobilePhone,param.getMobilePhone());
        if(isUpdate) {
            // 更新时，需要排除当前id
            lambdaQueryWrapper.ne(User::getId,param.getId());
        }
        long count = baseMapper.selectCount(lambdaQueryWrapper);
        if(count>0) {
            // 抛出一个手机号已存在异常
            ServiceException.throwBiz(SysErrEnum.MOBILE_PHONE_EXISTS);
        }
    }
    private LoginVO userToLoginVO(User user){
        LoginVO vo = new LoginVO();
        vo.setLastLoginTime(new Date());
        vo.setUserId(user.getId());
        vo.setUserName(user.getUserName());
        vo.setToken(null);
        vo.setAdminType(UserAdminTypeEnum.codeToEnum(user.getAdminType()));
        vo.setSuperAdmin(UserAdminTypeEnum.ADMIN.equals(vo.getAdminType()));
        if(UserAdminTypeEnum.ADMIN.equals(vo.getAdminType())) {
            vo.setPerms(CollectionUtil.newArrayList(UserAdminTypeEnum.ADMIN.name().toLowerCase()));
        } else {
            vo.setPerms(menuMapper.selectMenuCodeByUserId(user.getId()));
            // 设置部门信息
            Dept dept = deptMapper.selectById(user.getDeptId());
            if(dept!=null){
                vo.setDeptName(dept.getName());
            }
            // 设置角色id集合
            vo.setRoleIdList(userRoleMapper.selectList(
                    Wrappers.lambdaQuery(UserRole.class)
                    .eq(UserRole::getUserId,vo.getUserId())
            ).stream().map(item->item.getRoleId()).collect(Collectors.toList()));
            if(CollectionUtil.isNotEmpty(vo.getRoleIdList())){
                // 设置角色名称集合
                vo.setRoleNameList(roleMapper.selectList(
                        Wrappers.lambdaQuery(Role.class)
                                .in(Role::getId,vo.getRoleIdList())
                ).stream().map(item->item.getName()).collect(Collectors.toList()));
            }
        }
        // 分配权限
//        if(Integer.valueOf(1).equals(user.getUserType())) {
//            vo.setSuperAdmin(true);
//            vo.setPerms(new ArrayList<>());
//        } else if(Integer.valueOf(2).equals(user.getUserType())) {
//            vo.setSuperAdmin(false);
//            vo.setPerms(CollectionUtil.newArrayList("user:getById","role:page","role:getById"));
//        } else {
//            vo.setSuperAdmin(false);
//            vo.setPerms(new ArrayList<>());
//        }
        return vo;
    }
}
